library audio_output_device;

export 'src/bean/bean.dart';

import 'dart:async';
import 'dart:io';

// import 'package:advanced_audio_manager/advanced_audio_manager.dart';
import 'package:audio_session/audio_session.dart';
import 'package:av_audio_session/av_audio_session.dart';
import 'package:flutter/cupertino.dart';
// import 'package:videosdk/videosdk.dart';

import 'src/util/util.dart';
import 'src/bean/bean.dart' as bean;

typedef OnDevicesChanged = void Function(bean.AudioDevicesChangedEvent event);
typedef OnDevices = void Function(Set<bean.AudioDevice> devices);

class AudioOutputDevice with WidgetsBindingObserver{

  late final AudioSession _audioSession;

  StreamSubscription? _devicesChangedSubs, _devicesSubs;

  void enableLog({required bool enable}){
    Log.enableLog = enable;
  }

  OnDevices? _customOnDevices;
  Future<void> initAudioSession({
    OnDevicesChanged? onDevicesChanged,
    OnDevices? onDevices
  }) async{

    _customOnDevices = onDevices;
    // Log.log('setAppleAudioConfiguration');
    // VideoSDK.setAppleAudioConfiguration();
    
    // Log.log('getAudioDevices');
    // List<AudioDeviceInfo>? audioDevices = await VideoSDK.getAudioDevices();
    // if(audioDevices != null){
    //   for (var it in audioDevices) {
    //     Log.log(it.string);
    //   }
    // }

    if(Platform.isIOS){
      bool result = await AvAudioSession.setAppleAudioConfiguration(
          configuration: AppleAudioConfiguration(
              appleAudioCategory: AppleAudioCategory.playAndRecord,
              appleAudioCategoryOptions: {
                AppleAudioCategoryOption.allowBluetooth,
                AppleAudioCategoryOption.allowBluetoothA2DP
              },
              appleAudioMode: AppleAudioMode.videoChat
          )
      );
      Log.log('setAppleAudioConfiguration $result');

      // might wait for 1s to make sure config above really activated
      await Future.delayed(const Duration(seconds: 1));
    }

    // Log.log('start delay 10s');
    // await Future.delayed(const Duration(seconds: 10));
    // Log.log('delay 10s done');

    /*static void setAppleAudioConfiguration() async {
      await Helper.setAppleAudioConfiguration(AppleAudioConfiguration(
          appleAudioCategory: AppleAudioCategory.playAndRecord,
          appleAudioCategoryOptions: {
            AppleAudioCategoryOption.allowBluetooth,
            AppleAudioCategoryOption.allowBluetoothA2DP
          },
          appleAudioMode: AppleAudioMode.videoChat));
    }*/


    _audioSession = await AudioSession.instance;
    // _audioSession.configure(const AudioSessionConfiguration(
    //   avAudioSessionCategory: AVAudioSessionCategory.playback,
    //   avAudioSessionCategoryOptions: AVAudioSessionCategoryOptions.none,
    //   avAudioSessionMode: AVAudioSessionMode.defaultMode,
    //   avAudioSessionRouteSharingPolicy: AVAudioSessionRouteSharingPolicy.longFormAudio,
    //   avAudioSessionSetActiveOptions: AVAudioSessionSetActiveOptions.none,
    //   androidAudioAttributes: const AndroidAudioAttributes(
    //     contentType: AndroidAudioContentType.speech,
    //     flags: AndroidAudioFlags.none,
    //     usage: AndroidAudioUsage.voiceCommunication,
    //   ),
    //   androidAudioFocusGainType: AndroidAudioFocusGainType.gain,
    //   androidWillPauseWhenDucked: true,
    // ));
    _devicesChangedSubs = _audioSession.devicesChangedEventStream
        .map((e) => bean.AudioDevicesChangedEvent.from(e))
        .listen(onDevicesChanged??_onDevicesChanged);
    _devicesSubs = _audioSession.devicesStream
        .map((e) => e.map((it) => bean.AudioDevice.from(it)).toSet())
        .listen(onDevices??_onDevices);


    

    /*Log.log('start delay 10s');
    await Future.delayed(const Duration(seconds: 10));
    Log.log('delay 10s done');
    Log.log('getAudioDevices');
    audioDevices = await VideoSDK.getAudioDevices();
    if(audioDevices != null){
      for (var it in audioDevices) {
        Log.log(it.string);
      }
    }*/

    // FlutterAudioManager.setListener((){
    //   getInput();
    // });
    // getInput();
    WidgetsBinding.instance.addObserver(this);
  }

  // void getInput() async {
  //   AudioInput currentInput = await FlutterAudioManager.getCurrentOutput();
  //   Log.log("current:$currentInput");
  //   List<AudioInput> availableInputs = await FlutterAudioManager.getAvailableInputs();
  //   Log.log("available $availableInputs");
  // }

  void _onDevicesChanged(bean.AudioDevicesChangedEvent event){
    Log.log('Devices added:   ${event.devicesAdded}');
    Log.log('Devices removed: ${event.devicesRemoved}');
  }

  void _onDevices(Set<bean.AudioDevice> devices){
    Log.log('_onDevices');
    for (var it in devices) {
      Log.log('$it');
    }

  }

  Future<void> getAudioDevices() async{
    // AndroidAudioManager audioManager = AndroidAudioManager();
    // AndroidAudioDeviceInfo audioDeviceInfo = await audioManager.getCommunicationDevice();
    // Log.log('current audioDeviceInfo ${audioDeviceInfo.str}');
    // List<AndroidAudioDeviceInfo> list = await audioManager.getAvailableCommunicationDevices();
    // for (var it in list) {
    //   Log.log('audioDeviceInfo ${it.str}');
    // }
    // List<AndroidAudioDeviceInfo> deviceList = await audioManager.getDevices(AndroidGetAudioDevicesFlags.all);
    // for (var it in deviceList) {
    //   Log.log('device audioDeviceInfo ${it.str}');
    // }
    // return;
    Set<AudioDevice> devices = await _audioSession.getDevices();
    // Log.log('getAudioDevices');
    // for (var it in devices) {
    //   Log.log('$it');
    // }
    (_customOnDevices??_onDevices).call(devices
        .map((it) => bean.AudioDevice.from(it)).toSet()
    );
  }


  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    switch(state){
      /// the current state that user would like to know, they have to open
      /// the app first
      case AppLifecycleState.resumed:
        /// refresh state
        getAudioDevices();
        break;
      default:
        break;
    }
  }

  Future<void> close() async{
    WidgetsBinding.instance.removeObserver(this);
    await _devicesChangedSubs?.cancel();
    await _devicesSubs?.cancel();
  }

  static AudioDeviceType androidType2type(AndroidAudioDeviceType type) {
    switch (type) {
      case AndroidAudioDeviceType.unknown:
        return AudioDeviceType.unknown;
      case AndroidAudioDeviceType.builtInEarpiece:
        return AudioDeviceType.builtInEarpiece;
      case AndroidAudioDeviceType.builtInSpeaker:
        return AudioDeviceType.builtInSpeaker;
      case AndroidAudioDeviceType.wiredHeadset:
        return AudioDeviceType.wiredHeadset;
      case AndroidAudioDeviceType.wiredHeadphones:
        return AudioDeviceType.wiredHeadphones;
      case AndroidAudioDeviceType.lineAnalog:
        return AudioDeviceType.lineAnalog;
      case AndroidAudioDeviceType.lineDigital:
        return AudioDeviceType.lineDigital;
      case AndroidAudioDeviceType.bluetoothSco:
        return AudioDeviceType.bluetoothSco;
      case AndroidAudioDeviceType.bluetoothA2dp:
        return AudioDeviceType.bluetoothA2dp;
      case AndroidAudioDeviceType.hdmi:
        return AudioDeviceType.hdmi;
      case AndroidAudioDeviceType.hdmiArc:
        return AudioDeviceType.hdmiArc;
      case AndroidAudioDeviceType.usbDevice:
        return AudioDeviceType.usbAudio;
      case AndroidAudioDeviceType.usbAccessory:
        return AudioDeviceType.usbAudio;
      case AndroidAudioDeviceType.dock:
        return AudioDeviceType.dock;
      case AndroidAudioDeviceType.fm:
        return AudioDeviceType.fm;
      case AndroidAudioDeviceType.builtInMic:
        return AudioDeviceType.builtInMic;
      case AndroidAudioDeviceType.fmTuner:
        return AudioDeviceType.fmTuner;
      case AndroidAudioDeviceType.tvTuner:
        return AudioDeviceType.tvTuner;
      case AndroidAudioDeviceType.telephony:
        return AudioDeviceType.telephony;
      case AndroidAudioDeviceType.auxLine:
        return AudioDeviceType.auxLine;
      case AndroidAudioDeviceType.ip:
        return AudioDeviceType.ip;
      case AndroidAudioDeviceType.bus:
        return AudioDeviceType.bus;
      case AndroidAudioDeviceType.usbHeadset:
        return AudioDeviceType.usbAudio;
      case AndroidAudioDeviceType.hearingAid:
        return AudioDeviceType.hearingAid;
      case AndroidAudioDeviceType.builtInSpeakerSafe:
        return AudioDeviceType.builtInSpeakerSafe;
      case AndroidAudioDeviceType.remoteSubmix:
        return AudioDeviceType.remoteSubmix;
    }
  }
}

// extension AudioDeviceInfoString on AudioDeviceInfo{
//   String get string => 'deviceId: $deviceId, groupId: $groupId, label: $label,'
//       ' kind: $kind';
// }

extension AndroidAudioDeviceInfoExtension on AndroidAudioDeviceInfo{
  String get str => "AudioDevice(id:${id.toString()},name:$productName,isInput:$isSource,isOutput:$isSink,type:${AudioOutputDevice.androidType2type(type)}";
}


